﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.IO;
using Microsoft.Win32;

namespace MyUninstaller7 {
    public partial class UninstallForm : Form {
        private class RecordItem {
            public string Path;
            public bool StillExists;
            public EItemType Type;
            public bool Checked = true;
            public RecordItem(string path) {
                Path = path;
                Type = Utils.utils.GetType(path);
            }
        }
        private List<RecordItem> recordItems = new List<RecordItem>();
        private bool _ShowingInstalled;

        private RecordStore.Record record;
        public UninstallForm(RecordStore.Record _record, bool Installed) {
            InitializeComponent();
            record = _record;
            _ShowingInstalled = Installed;
            Text = (Installed ? "Installed items" : "Deleted items") + " \"" + Path.GetFileName(record.fileName) + "\"";
            label1.Text = (_ShowingInstalled ? "Installation" : "Deletion") + " log for '" + _record.DisplayName + "':";
            if (!Installed) {
                for (int i=1; i<toolStrip1.Items.Count; ++i)
                    toolStrip1.Items[i].Visible = false;
                listView1.CheckBoxes = false;
                button2.Visible = false;
                button3.Visible = false;
            }
            for (int i = 0; i < 4; ++i)
                imageList1.Images.Add(Utils.utils.FadeImage((Image)imageList1.Images[i].Clone()));
            foreach (string s in (Installed ? _record.newItems : _record.deletedItems))
                recordItems.Add(new RecordItem(s));
            PopulateItems();
        }

        private bool CreateScript(TextWriter stream) {
            stream.WriteLine(":: This script was autogenerated by My Uninstaller 7\n::");
            stream.WriteLine("@echo off\n");
            stream.WriteLine("echo Uninstallation target: '" + record.DisplayName + "'\necho.\n");
            if (record.UninstallEntries().Count == 0) {
                stream.WriteLine("echo Native uninstallation command was not automatically detected.");
                stream.WriteLine("echo It is recommended to run the program uninstallation manually from Add/Remove, ");
                stream.WriteLine("echo    if the entry is still there.");
                stream.WriteLine("pause");
                stream.WriteLine("echo.\n");
            }
            foreach (string uninst in record.UninstallValuesOf("UninstallString")) {
                stream.WriteLine("echo Running " + uninst);
                stream.WriteLine("start /w \"\" " + uninst);
            }
            stream.WriteLine("echo Warning: Uninstallation will begin now. This process is irreversible.");
            stream.WriteLine("echo There will bo no further warnings. Close window now to cancel.");
            stream.WriteLine("pause\n");
            foreach (RecordItem item in recordItems) {
                if (!item.Checked) continue;
                stream.WriteLine("echo Removing " + item.Path);
                if (item.Type == EItemType.RegistryKey) stream.WriteLine("reg delete \"" + item.Path.Substring(0, item.Path.Length - 1) + "\" /f");
                else if (item.Type == EItemType.RegistryValue) {
                    MyTuple<string, string> split = Utils.utils.SplitParent(item.Path);
                    stream.WriteLine("reg delete \"" + split.Item1.Substring(0, split.Item1.Length - 1) + "\" /v \"" + split.Item2.Replace(@"\", @"\\") + "\" /f");
                }
                else if (item.Type == EItemType.Folder) stream.WriteLine("rmdir /s /q \"" + item.Path + "\"");
                else if (item.Type == EItemType.File) stream.WriteLine("del /f \"" + item.Path + "\"");
            }
            stream.WriteLine("echo.");
            stream.WriteLine("echo Done!");
            stream.WriteLine("pause");
            return true;
        }

        private void PerformUninstall() {
            bool nativeProcessRan = false;
            while (true) {
                List<string> uninstCmds = record.UninstallValuesOf("UninstallString");
                if (uninstCmds.Count == 0) break;
                DialogResult dresult = MessageBox.Show("The entries this application has created for automatic uninstallation are still present. " +
                    "It is recommended that the native uninstallation is run first. " +
                    "Do you want to run them now?\n\n" +
                    uninstCmds.Select(a=>("Uninstallation command: '"+a+"'")).Aggregate((a, b) => a + "\n" + b),
                    "Uninstallation",
                    MessageBoxButtons.YesNoCancel,
                    MessageBoxIcon.Question);
                if (dresult == DialogResult.Cancel) return;
                else if (dresult == DialogResult.No) break;
                nativeProcessRan = true;
                // need to separate command and argument
                string progName=uninstCmds[0].Trim(), argument = "";
                int splitPos = -1;
                if (progName[0] == '"')
                    splitPos = progName.IndexOf('"', 1);
                else splitPos = progName.IndexOf(' ');
                if (splitPos > -1) {
                    argument = progName.Substring(splitPos + 1);
                    progName = progName.Substring(0, splitPos + 1).Trim();
                }
                Process puninst = Process.Start(progName, argument);
                while (true) {
                    MessageBox.Show("Native uninstallation process is running. Please click Ok when the process completes.",
                        "Uninstaller 7",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information);
                    if (puninst.HasExited) break;
                    dresult = MessageBox.Show("It appears that the process '" + uninstCmds[0] + "'has not yest terminated! Continue anyway?",
                        "Uninstaller 7",
                        MessageBoxButtons.YesNoCancel,
                        MessageBoxIcon.Exclamation,
                        MessageBoxDefaultButton.Button2);
                    if (dresult == DialogResult.Cancel) return;
                    else if (dresult == DialogResult.Yes) break;
                }
            }
            if (nativeProcessRan) PopulateItems();
            // Now that the native uninstallation is taken care of, here begins the removal of entries part
            if (recordItems.All(a => !a.Checked)) {
                MessageBox.Show("There is nothing " 
                    + (recordItems.Any(a => a.StillExists)?"marked":"remaining")
                    + " to uninstall.",
                    "Uninstaller 7",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Information);
                return;
            }
            if (MessageBox.Show("This will begin the uninstallation. There will be no further warning. Press Ok to continue.",
                "Uninstaller 7",
                MessageBoxButtons.OKCancel,
                MessageBoxIcon.Exclamation) == DialogResult.Cancel) return;
            foreach (RecordItem record in recordItems) {
                if (!record.Checked) continue;
                if (record.Type == EItemType.RegistryKey) {
                    MyTuple<string, string> split = Utils.utils.SplitParent(record.Path);
                    RegistryKey rk = Utils.utils.OpenRegKey(split.Item1, true);
                    if (rk != null) {
                        try {
                            rk.DeleteSubKeyTree(split.Item2);
                        }
                        catch (Exception) { }
                        rk.Close();
                    }
                }
                else if (record.Type == EItemType.RegistryValue) {
                    MyTuple<string, string> split = Utils.utils.SplitParent(record.Path);
                    RegistryKey rk = Utils.utils.OpenRegKey(split.Item1, true);
                    if (rk != null) {
                        try {
                            rk.DeleteValue(split.Item2);
                        } catch (Exception) { }
                        rk.Close();
                    }
                }
                else if (record.Type == EItemType.Folder) {
                    try {
                        Directory.Delete(record.Path, true);
                    } catch { }
                }
                else if (record.Type == EItemType.File) {
                    File.Delete(record.Path);
                }
            }
            PopulateItems();
            string finalMsg;
            MessageBoxIcon finalIcon = MessageBoxIcon.Information;
            if (recordItems.Any(a => a.StillExists)) {
                if (recordItems.Any(a => a.Checked && a.StillExists)) {
                    finalIcon = MessageBoxIcon.Warning;
                    finalMsg = "Some of the checked items are still remaining.";
                }
                else finalMsg = "All checked items have been uninstalled successfully, but the unchecked items are still present.";
            }
            else finalMsg = "This software has been uninstalled completely.";
            MessageBox.Show(finalMsg, "Uninstaller 7", MessageBoxButtons.OK, finalIcon);
        }

        // This scans for existance, sorts, and populates the list view
        private void PopulateItems() {
            foreach (RecordItem rec in recordItems)
                rec.StillExists = Utils.utils.Exists(rec.Path);
            recordItems.Sort((a, b) => {
                if (a.StillExists && !b.StillExists) return -1;
                else if (!a.StillExists && b.StillExists) return 1;
                else if (a.Type == b.Type) return a.Path.CompareTo(b.Path);
                else return a.Type.CompareTo(b.Type);
            });
            listView1.Items.Clear();
            foreach (RecordItem rec in recordItems) {
                int imageIndex;
                switch (rec.Type) {
                    case EItemType.RegistryKey:
                        imageIndex = 0; break;
                    case EItemType.RegistryValue:
                        imageIndex = 1; break;
                    case EItemType.Folder:
                        imageIndex = 2; break;
                    case EItemType.File:
                        imageIndex = 3; break;
                    default:
                        Debug.Fail("Unknown EItemType encountered while choosing imageIndex.");
                        imageIndex = 0;
                        break;
                }
                if (!rec.StillExists) imageIndex += 4;
                // Adding it triggers the Checked
                bool isChecked = rec.Checked;
                ListViewItem listItem = listView1.Items.Add(rec.Path, imageIndex);
                if (!rec.StillExists) isChecked = false;
                listItem.Checked = isChecked;
                // The above line does not automatically set rec.Checked
                rec.Checked = isChecked;
                if (!rec.StillExists)
                    listItem.ForeColor = Color.Gray;
                if (rec.Type == EItemType.RegistryKey) listItem.BackColor = Color.LightYellow;
                else if (rec.Type == EItemType.Folder) listItem.BackColor = Color.LightYellow;
                else listItem.BackColor = Color.White;
            }
            columnHeader1.AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
        }

        private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e) {
            if (!recordItems[e.Item.Index].StillExists) e.Item.Checked = false;
            else recordItems[e.Item.Index].Checked = e.Item.Checked;
        }

        private void toolStripButton1_Click(object sender, EventArgs e) {
            PopulateItems();
        }

        private void markUnmark_Click(object sender, EventArgs e) {
            // Mark/unmark all
            bool selectedOnly = sender.Equals(button3);
            bool anyChecked = false;
            for (int i = 0; i < recordItems.Count; ++i) {
                if (!selectedOnly || listView1.Items[i].Selected) {
                    if (recordItems[i].Checked && recordItems[i].StillExists) {
                        anyChecked = true;
                        break;
                    }
                }
            }
            bool desiredState = !anyChecked;
            for (int i=0; i<recordItems.Count; ++i) {
                RecordItem rec=recordItems[i];
                if ((!selectedOnly || listView1.Items[i].Selected) && rec.StillExists) {
                    rec.Checked = desiredState;
                    listView1.Items[i].Checked = desiredState;
                }
            }
        }

        private void toolStripButton2_Click(object sender, EventArgs e) {
            if (recordItems.All(a => !a.Checked)) {
                MessageBox.Show("Cannot create script since none of the entries is selected to be in there.",
                    "Uninstaller 7",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);
                return;
            }
            saveFileDialog1.FileName = "Uninstall '" + record.DisplayName + "'";
            if (saveFileDialog1.ShowDialog() == DialogResult.OK) {
                //MessageBox.Show(saveFileDialog1.FileName);
                using (StreamWriter sw = new StreamWriter(saveFileDialog1.FileName)) {
                    if (CreateScript(sw))
                        MessageBox.Show("An uninstallation script has been created based on the currently selected entries and the current state of the software.",
                            "Uninstaller 7",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Information);
                    else MessageBox.Show("Error encountered while creating uninstallation script.",
                        "Uninstaller 7",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Warning);
                }
            }
        }

        private void toolStripButton3_Click(object sender, EventArgs e) {
            PerformUninstall();
        }
    }
}
